#include "game.h"
#include <algorithm>
#include <jsoncpp/json/json.h>

Snake::Snake(int id, const Position& startPos, int color) 
    : id_(id), direction_(Direction::RIGHT), status_(GameStatus::OK), 
      score_(0), color_(color), speed_(200) {
    for (int i = 0; i < 5; ++i) {
        body_.push_back({startPos.x + i * 2, startPos.y});
    }
}

void Snake::changeDirection(Direction newDir) {
    if ((direction_ == Direction::UP && newDir != Direction::DOWN) ||
        (direction_ == Direction::DOWN && newDir != Direction::UP) ||
        (direction_ == Direction::LEFT && newDir != Direction::RIGHT) ||
        (direction_ == Direction::RIGHT && newDir != Direction::LEFT)) {
        direction_ = newDir;
    }
}

void Snake::move() {
    if (status_ != GameStatus::OK) return;
    
    Position newHead = body_.front();
    switch (direction_) {
        case Direction::UP:    newHead.y--; break;
        case Direction::DOWN:  newHead.y++; break;
        case Direction::LEFT:  newHead.x -= 2; break;
        case Direction::RIGHT: newHead.x += 2; break;
    }
    
    body_.insert(body_.begin(), newHead);
    body_.pop_back();
}

bool Snake::checkCollision(const Position& pos) const {
    return std::find(body_.begin(), body_.end(), pos) != body_.end();
}

bool Snake::checkSelfCollision() const {
    if (body_.size() < 5) return false;
    auto head = body_.front();
    return std::find(body_.begin() + 1, body_.end(), head) != body_.end();
}

bool Snake::checkWallCollision(int mapWidth, int mapHeight) const {
    const auto& head = body_.front();
    return head.x <= 0 || head.x >= mapWidth - 2 || 
           head.y <= 0 || head.y >= mapHeight - 1;
}

void Snake::grow() {
    body_.push_back(body_.back());
}

Game::Game(int width, int height) 
    : width_(width), height_(height), gen_(rd_()), 
      xDist_(2, width - 4), yDist_(1, height - 2) {
    generateFood();
}

void Game::addPlayer(int playerId) {
    std::lock_guard<std::mutex> lock(mutex_);
    
    // Assign color based on player ID
    int color = (playerId % 2 == 0) ? 1 : 2; // 1: blue, 2: green
    
    // Find a suitable starting position
    Position startPos;
    do {
        startPos.x = xDist_(gen_);
        startPos.y = yDist_(gen_);
        // Ensure x is even for alignment
        if (startPos.x % 2 != 0) startPos.x++;
    } while (!isPositionFree(startPos));
    
    snakes_.push_back(std::make_unique<Snake>(playerId, startPos, color));
}

void Game::removePlayer(int playerId) {
    std::lock_guard<std::mutex> lock(mutex_);
    snakes_.erase(std::remove_if(snakes_.begin(), snakes_.end(), 
        [playerId](const std::unique_ptr<Snake>& snake) {
            return snake->getId() == playerId;
        }), snakes_.end());
}

void Game::changeDirection(int playerId, Direction dir) {
    std::lock_guard<std::mutex> lock(mutex_);
    for (auto& snake : snakes_) {
        if (snake->getId() == playerId) {
            snake->changeDirection(dir);
            break;
        }
    }
}

void Game::update() {
    std::lock_guard<std::mutex> lock(mutex_);
    
    // Move all snakes
    for (auto& snake : snakes_) {
        if (snake->getStatus() == GameStatus::OK) {
            snake->move();
            
            // Check for collisions
            if (snake->checkSelfCollision()) {
                snake->setStatus(GameStatus::KILL_BY_SELF);
            }
            if (snake->checkWallCollision(width_, height_)) {
                snake->setStatus(GameStatus::KILL_BY_WALL);
            }
            
            // Check for food
            const auto& head = snake->getBody().front();
            auto foodIt = std::find_if(foods_.begin(), foods_.end(), 
                [&head](const Food& food) {
                    return food.getPosition() == head;
                });
            
            if (foodIt != foods_.end()) {
                snake->grow();
                snake->increaseScore(10);
                foods_.erase(foodIt);
                generateFood();
            }
        }
    }
}

std::string Game::getGameState() const {
    std::lock_guard<std::mutex> lock(mutex_);
    
    Json::Value state;
    state["width"] = width_;
    state["height"] = height_;
    
    // Add snakes
    Json::Value snakesArray;
    for (const auto& snake : snakes_) {
        Json::Value snakeObj;
        snakeObj["id"] = snake->getId();
        snakeObj["color"] = snake->getColor();
        snakeObj["score"] = snake->getScore();
        snakeObj["status"] = static_cast<int>(snake->getStatus());
        
        Json::Value bodyArray;
        for (const auto& pos : snake->getBody()) {
            Json::Value posObj;
            posObj["x"] = pos.x;
            posObj["y"] = pos.y;
            bodyArray.append(posObj);
        }
        snakeObj["body"] = bodyArray;
        snakesArray.append(snakeObj);
    }
    state["snakes"] = snakesArray;
    
    // Add foods
    Json::Value foodsArray;
    for (const auto& food : foods_) {
        Json::Value foodObj;
        const auto& pos = food.getPosition();
        foodObj["x"] = pos.x;
        foodObj["y"] = pos.y;
        foodsArray.append(foodObj);
    }
    state["foods"] = foodsArray;
    
    Json::StreamWriterBuilder builder;
    return Json::writeString(builder, state);
}

void Game::generateFood() {
    while (foods_.size() < 10) {
        Position pos;
        do {
            pos.x = xDist_(gen_);
            pos.y = yDist_(gen_);
            if (pos.x % 2 != 0) pos.x++; // Ensure x is even
        } while (!isPositionFree(pos));
        
        foods_.emplace_back(pos.x, pos.y);
    }
}

bool Game::isPositionFree(const Position& pos) const {
    // Check against snakes
    for (const auto& snake : snakes_) {
        if (snake->checkCollision(pos)) {
            return false;
        }
    }
    
    // Check against foods
    for (const auto& food : foods_) {
        if (food.getPosition() == pos) {
            return false;
        }
    }
    
    return true;
}